﻿using UnityEngine;
using UnityEngine.Extension;
using System.Collections;
using System;
using System.Extension;
using System.Collections.Generic;
using System.Reflection;

namespace UnityEditor.Extension
{
    public static class SerializedPropertyExtension
    {
		public static void ForEachVisibleProperty(this SerializedProperty source, Action<SerializedProperty> action)
        {
            SerializedProperty sourceCopy = source.Copy();
            int depth = sourceCopy.depth + 1;
            while (sourceCopy.NextVisible(true))
            {
                if (sourceCopy.depth < depth) break;  // < 1 - above root element, to include child field and avoid next field 
                action(sourceCopy);
            }
        }
        public static void ForEachProperty(this SerializedProperty source, Action<SerializedProperty> action)
        {
            SerializedProperty sourceCopy = source.Copy();
            int depth = sourceCopy.depth + 1;
            while (sourceCopy.Next(true))
            {
                if (sourceCopy.depth < depth) break;  // < 1 - above root element, to include child field and avoid next field 
                action(sourceCopy);
            }
        }
        public static int  VisiblePropertyCount(this SerializedProperty source)
        {
            int count = 1; // 1 - for header title count;
            source.ForEachVisibleProperty(f => count++);
            return count;
        }
        public static int  PropertyCount(this SerializedProperty source)
        {
            int count = 1; // 1 - for header title count;
            source.ForEachProperty(f => count++);
            return count;
        }
        public static bool isType(this SerializedProperty property, List<SerializedPropertyType> types)
        {
            return types.Contains(property.propertyType);
        }
        public static bool isPrimitive(this SerializedProperty property)
        {
            List<SerializedPropertyType> primitiveTypes = new List<SerializedPropertyType>() { SerializedPropertyType.Boolean, 
                SerializedPropertyType.Character, SerializedPropertyType.Enum, SerializedPropertyType.Float, SerializedPropertyType.Integer,
                SerializedPropertyType.String};
            return property.isType(primitiveTypes);
        }
        public static bool isEnumerable(this SerializedProperty property)
        {
            List<SerializedPropertyType> enumerableTypes = new List<SerializedPropertyType>() { SerializedPropertyType.ArraySize, 
                SerializedPropertyType.Generic };
            return property.isType(enumerableTypes);
        }

        public static void ForEachItem(this SerializedProperty property, string Title, Func<SerializedProperty, bool> callBack)
        {
            bool hasTitle = Title != "";
            if (hasTitle)
            {
                GUILayout.BeginHorizontal();
                property.isExpanded = GUILayout.Toggle(property.isExpanded, Title, EditorStyles.miniButtonLeft);
                if (GUILayout.Button("+", EditorStyles.miniButtonRight, GUILayout.Width(16)))
                    property.AppendItemToArray();
                GUILayout.EndHorizontal();
            }
            if (property.isExpanded || !hasTitle)
                for (int i = 0; i < property.arraySize; i++)
                    if(callBack(property.GetArrayElementAtIndex(i)))
                        property.DeleteArrayElementAtIndex(i);
        }

        public static void ForEachItemInFold(this SerializedProperty property, string Title, Action<SerializedProperty> callBack)
        {
            GUILayout.BeginHorizontal();
            property.isExpanded = GUILayout.Toggle(property.isExpanded, "", EditorStyles.foldout, GUILayout.Width(16));
            GUILayout.Label(Title);
            if (GUILayout.Button("+", EditorStyles.miniButtonRight, GUILayout.Width(16)))
                property.AppendItemToArray();
            GUILayout.EndHorizontal();

            if (property.isExpanded)
            {
                property.ForEachItem("", i =>
                {
                    bool deleteLink = false;

                    GUILayout.BeginHorizontal();
                    GUILayout.Label("", GUILayout.Width(16));
                    callBack(i);
                    deleteLink = GUILayout.Button("-", EditorStyles.miniButtonRight, GUILayout.Width(16));
                    GUILayout.EndHorizontal();
                    return deleteLink;
                });
            }
        }

        public static SerializedProperty AppendItemToArray(this SerializedProperty property)
        {
            int length = property.arraySize;
            property.InsertArrayElementAtIndex(length);
            return property.GetArrayElementAtIndex(length);
        }

        public static T GetComponentInParent<T>(this SerializedProperty property) where T : Component
        {
            T result = null;
            MonoBehaviour component = property.serializedObject.targetObject as MonoBehaviour;
            T[] parent = component.GetComponentsInParent<T>();
            T root = component.GetComponent<T>();
            foreach(T pcomp in parent)
            {
                if(pcomp != root)
                {
                    result = pcomp;
                    break;
                }
            }
            return result;
        }
    }

}


/*
 static BindingFlags preferedFlag = BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly | BindingFlags.Static;
        public static List<string> GetPath(this object source, string rPath = "", params Type[] inclusives)
        {
            List<string> result = new List<string>();
            Type sourceType = source.GetType();
            MemberInfo[] members = sourceType.GetMembers(preferedFlag);
            List<MemberInfo> validMembers = new List<MemberInfo>();
            foreach (MemberInfo member in members)
            {
                if (member.MemberType == MemberTypes.Property || member.MemberType == MemberTypes.Field)
                {
                    if (!member.IsSameOrSubclass(typeof(UnityEngine.Object)) && !member.isGeneric())
                    {
                        if(member.isClass())
                            validMembers.Add(member);
                        else if (member.isInclusive(inclusives) || inclusives.Length < 1)
                            result.Add(rPath + member.Name);
                    }
                }
            }
            foreach(MemberInfo member in validMembers)
            {
                object value = member.GetValue(source);
                if (value != null)
                    result.AddRange(value.GetPath(rPath + member.Name + "/", inclusives));
            }

            if(source is GameObject)
            {
                UnityEngine.Object[] components = (source as GameObject).GetComponents<Component>();
                foreach(UnityEngine.Object obj in components)
                    result.AddRange(obj.GetPath(obj.GetType().Name + "/", inclusives));
            }
            return result;
        }
*/
